#include "vobject_complex.h"
#include "../file/config.h"
#include "../file/file.h"


using namespace TerraGE;

VObject_Complex::VObject_Complex(Bitmap_Manager *bm, Camera *cam, string folder, float tx, float ty, float tz, float sc, bool shad) {
    path = "assets/objects/" + folder +"/";

    actual_bit = bm->Get_Bitmap(path+"diffuse.png")->Get_Bit();

    actual_bit_shadow = NULL;


    Config *cfg;
    cfg = new Config(path+"settings.ini");
    tall = cfg->Get_Float("General", "Height", 500);
    c_x = cfg->Get_Float("General", "Center_X", 100);
    c_y = cfg->Get_Float("General", "Center_Y", 100);

    cfg->Save();
    delete cfg;

    scale = sc;

    x = tx;
    y = ty;
    z = tz;
    w = al_get_bitmap_width(actual_bit);
    h = al_get_bitmap_height(actual_bit);

    hover = false;

    tint = al_map_rgb(255, 255, 255);
    angle = 0;

    flags = 0;
    selected=false;
    visible = true;
    light = NULL;

    type = VOBJECT_COMPLEX;
    animation = NULL;

    if (File_Exists(path+"animations.ini")) {
        animation = new Animation_Manager(bm, path);

        actual_bit = animation->Get_Actual_Bitmap();
        actual_bit_normal = animation->Get_Actual_Bitmap_Normal();
        actual_bit_height = animation->Get_Actual_Bitmap_Height();
    }
    else {
        actual_bit_normal = bm->Get_Bitmap(path+"normal.png")->Get_Bit();
        actual_bit_height = bm->Get_Bitmap(path+"height.png")->Get_Bit();

        has_shadow = shad;
        if (shad) {
            if (File_Exists(path+"shadow.png")) actual_bit_shadow = bm->Get_Bitmap(path+"shadow.png")->Get_Bit();
            else {
                Generate_Shadow_Bit(cam);
                if (actual_bit_shadow) {
                    al_save_bitmap((path+"shadow.png").c_str(), actual_bit_shadow);
                    bm->Add_Bitmap((path+"shadow.png"), actual_bit_shadow);
                }
            }

        }
    }
}


void VObject_Complex::Generate_Normal_Map(Bitmap_Manager *bm) {
    ALLEGRO_BITMAP *normal, *height, *final;
    al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
    normal = bm->Get_Bitmap(path+"normal.png")->Get_Bit();
    height = bm->Get_Bitmap(path+"height.png")->Get_Bit();

    final = al_create_bitmap(w, h);


    ALLEGRO_LOCKED_REGION *bg_lock, *fg_lock;
    uint32_t *bg_data, *fg_data;
    int bg_h, bg_w, bg_x, bg_y;
    int fg_h, fg_w;


    bg_h = al_get_bitmap_height(normal);
    bg_w = al_get_bitmap_width(normal);

    fg_h = al_get_bitmap_height(height);
    fg_w = al_get_bitmap_width(height);

    bg_lock = al_lock_bitmap(normal, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READWRITE);
    fg_lock = al_lock_bitmap(height, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READWRITE);

    bg_data = (uint32_t *)bg_lock->data;
    fg_data = (uint32_t *)fg_lock->data;

    ALLEGRO_BITMAP *target;
    target = al_get_target_bitmap();
    al_set_target_bitmap(final);
    al_clear_to_color(al_map_rgba(0, 0, 0, 0));
    for (bg_y = 0; bg_y < bg_h; bg_y++)
    {
        for (bg_x = 0; bg_x < bg_w; bg_x++)
        {
            uint32_t *bg_p;
            uint32_t *fg_p;
            unsigned char R1, G1, B1, A2;

            bg_p = (uint32_t *) ((uint8_t*)&bg_data[bg_x] + bg_lock->pitch * (bg_y));
            R1 = (*bg_p & 0x00FF0000) >> 16;
            G1 = (*bg_p & 0x0000FF00) >>  8;
            B1 = (*bg_p & 0x000000FF) >>  0;

            fg_p = (uint32_t *) ((uint8_t*)&fg_data[bg_x] + fg_lock->pitch * (bg_y));
            A2 = (*fg_p & 0x00FF0000) >> 16;

            al_put_pixel(bg_x, bg_y, al_map_rgba(R1, G1, B1, A2));
        }
    }
    al_set_target_bitmap(target);

    al_unlock_bitmap(normal);
    al_unlock_bitmap(height);

    al_destroy_bitmap(normal);
    al_destroy_bitmap(height);

    al_set_new_bitmap_flags(0);

    actual_bit_normal = al_clone_bitmap(final);
    al_destroy_bitmap(final);
}


void VObject_Complex::Generate_Shadow_Bit(Camera *cam) {
    actual_bit_shadow = al_create_bitmap(w, h);
    ALLEGRO_BITMAP *target;
    target = al_get_target_bitmap();


    ALLEGRO_LOCKED_REGION *bg_lock;
    ALLEGRO_LOCKED_REGION *fg_lock;
    ALLEGRO_LOCKED_REGION *h_lock;

    fg_lock = al_lock_bitmap(actual_bit, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READONLY);
    bg_lock = al_lock_bitmap(actual_bit_shadow, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READWRITE);
    h_lock = al_lock_bitmap(actual_bit_height, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READONLY);

    uint32_t *fg_data, *bg_data, *h_data;
    fg_data = (uint32_t *)fg_lock->data;
    bg_data = (uint32_t *)bg_lock->data;
    h_data = (uint32_t *)h_lock->data;

    al_set_target_bitmap(actual_bit_shadow);
    al_clear_to_color(al_map_rgba_f(0, 0, 0, 0));

    for (int x=0; x<w; x++) {
        for (int y=0; y<h; y++) {
            uint32_t *h_p;
            uint32_t *fg_p;
            uint32_t *bg_p;
            unsigned char r, g, b, a;
            h_p = (uint32_t *) ((uint8_t*)&fg_data[x] + fg_lock->pitch * y);
            a = (*h_p & 0xFF000000) >> 24;
            if (a < 254) continue;

            h_p = (uint32_t *) ((uint8_t*)&h_data[x] + h_lock->pitch * y);
            a = (*h_p & 0x00FF0000) >> 16;

            Vec2D pos;
            float z;
            z = (float)a/255.0*tall/cam->Get_Height_Factor();
            pos = cam->Screen_To_Proj(x, y, z);
            pos = cam->Proj_To_Screen(pos.x, pos.y, 0);


            unsigned char nr, ng, nb, na;
            assert(z < 65536);

            ng = z / 255;
            nr = z-ng*255;

            if ((pos.x >= 0) && (pos.x < w) && (pos.y >= 0) && (pos.y < h)) {
                bg_p = (uint32_t *) ((uint8_t*)&bg_data[(int)pos.x] + bg_lock->pitch * (int)pos.y);
                a = (*bg_p & 0xFF000000) >> 24;
                r = (*bg_p & 0x00FF0000) >> 16;
                g = (*bg_p & 0x0000FF00) >> 8;

                if (z < (r+g*255)) continue;
            }

            al_put_pixel(pos.x, pos.y, al_map_rgba(nr, ng, 0, 255));
            al_put_pixel(pos.x-1, pos.y, al_map_rgba(nr, ng, 0, 255));
            al_put_pixel(pos.x+1, pos.y, al_map_rgba(nr, ng, 0, 255));
            al_put_pixel(pos.x, pos.y+1, al_map_rgba(nr, ng, 0, 255));
            al_put_pixel(pos.x, pos.y-1, al_map_rgba(nr, ng, 0, 255));
        }
    }


    // Cleanup Garbage
    for (int x=0; x<w; x++) {
        for (int y=0; y<h; y++) {
            uint32_t *h_p;
            uint32_t *bg_p;
            unsigned char r, g, b, a;
            bg_p = (uint32_t *) ((uint8_t*)&bg_data[x] + bg_lock->pitch * y);
            a = (*bg_p & 0xFF000000) >> 24;
            r = (*bg_p & 0x00FF0000) >> 16;
            g = (*bg_p & 0x0000FF00) >> 8;
            b = (*bg_p & 0x000000FF) >> 0;
            if (a < 250) {
                int empty_pixels=0;
                unsigned char tr, tg, tb, ta;
                for (int tx=-1; tx<=1; tx++) {
                    for (int ty=-1; ty<=1; ty++) {
                        if ((tx==0) && (ty==0)) continue;

                        if ((x+tx) < 0) continue;
                        if ((y+ty) < 0) continue;
                        if ((x+tx) >= w) continue;
                        if ((y+ty) >= h) continue;

                        h_p = (uint32_t *) ((uint8_t*)&bg_data[x+tx] + bg_lock->pitch * (y+ty));
                        ta = (*h_p & 0xFF000000) >> 24;
                        tr = (*h_p & 0x00FF0000) >> 16;
                        tg = (*h_p & 0x0000FF00) >> 8;
                        tb = (*h_p & 0x000000FF) >> 0;

                        if (ta<255) empty_pixels++;
                    }
                }

                if (empty_pixels < 4) {
                    al_put_pixel(x, y, al_map_rgba(tr, tg, 0, 255));
                }
            }
            else {
                int full_pixels=0;
                for (int tx=-1; tx<=1; tx++) {
                    for (int ty=-1; ty<=1; ty++) {
                        if ((tx==0) && (ty==0)) continue;

                        if ((x+tx) < 0) continue;
                        if ((y+ty) < 0) continue;
                        if ((x+tx) >= w) continue;
                        if ((y+ty) >= h) continue;

                        unsigned char tr, tg, tb, ta;
                        h_p = (uint32_t *) ((uint8_t*)&bg_data[x+tx] + bg_lock->pitch * (y+ty));
                        ta = (*h_p & 0xFF000000) >> 24;
                        tr = (*h_p & 0x00FF0000) >> 16;
                        tg = (*h_p & 0x0000FF00) >> 8;
                        tb = (*h_p & 0x000000FF) >> 0;

                        if (ta>250) full_pixels++;
                    }
                }

                if (full_pixels < 4) {
                    al_put_pixel(x, y, al_map_rgba(0, 0, 0, 0));
                }
            }
        }
    }


    al_unlock_bitmap(actual_bit_shadow);
    al_unlock_bitmap(actual_bit_height);
    al_unlock_bitmap(actual_bit);

    al_set_target_bitmap(target);
}


void VObject_Complex::Update(float timestep) {
    if (light) {
        light->Set_X(x);
        light->Set_Y(y);
        light->Set_Z(z);
    }

    if (animation) {
        animation->Update(timestep);

        actual_bit = animation->Get_Actual_Bitmap();
        actual_bit_normal = animation->Get_Actual_Bitmap_Normal();
        actual_bit_height = animation->Get_Actual_Bitmap_Height();
    }
}
